/*
 * JugadorAlumno.java
 *
 * Created on 16 de octubre de 2009, 22:37
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package cuatroenraya.jugadores;

import cuatroenraya.Jugador;
import java.util.Vector;

/**
 * @version 3.0.0
 * @author Alonso Gonzalo - Pereira Analía - Suarez María Dolores.
 */
public class JugadorAlumno extends Jugador{

    private String integrantes;
    private String grupo;
    private String curso;
    private int jugador;
    private int jugadorOponente;
    private int masinfinito;
    private int menosinfinito;
    private int cantNiveles;
    private int nivelActual;


    /** Creates a new instance of JugadorAlumno */
    public JugadorAlumno() {
        integrantes = "Alonso-Pereira-Suarez";
        grupo = " ";
        curso = "5k3";
        jugador = 1;
        jugadorOponente = 2;
        masinfinito = 1000;
        menosinfinito = -1000;
        cantNiveles = 10;
        nivelActual = 0;
    }

    /** Creates a new instance of JugadorAlumno */
    public JugadorAlumno(int jugador, int jugadorOponente, int cantNiveles) {
        integrantes = "Alonso-Pereira-Suarez";
        grupo = " ";
        curso = "5k3";
        this.jugador = jugador;
        this.jugadorOponente = jugadorOponente;
        masinfinito = 1000;
        menosinfinito = -1000;
        this.cantNiveles = cantNiveles;
        nivelActual = 0;
    }

    /** Creates a new instance of JugadorAlumno */
    public JugadorAlumno(String integrantes, String grupo, String curso, int jugador, int oponente) {
        this.integrantes = integrantes;
        this.grupo = grupo;
        this.curso = curso;
        this.jugador = jugador;
        this.jugadorOponente = oponente;
        masinfinito = 1000;
        menosinfinito = -1000;
    }

    /**
     * Metodo que devuelve los integrantes.
     * @return integrantes.
     */
    public String devolverIntegrantes(){
        return integrantes;
    }

    /**
     * Metodo que devuelve el numero de grupo.
     * @return grupo.
     */
    public String devolverGrupo(){
        return grupo;
    }

    /**
     * Metodo que devuelve el curso.
     * @return curso.
     */
    public String devolverCurso(){
        return curso;
    }

    /**
     * Metodo que devuelve la posicion en la que colocara la ficha el jugadorAlumno.
     * Este metodo genera un arbol con nodos Max en el primer nivel, es decir nodos
     * que representan las jugadas de JugadorAlumno y en el segundo nivel genera
     * nodos Min, es decir nodos que representan las jugadas del jugador oponente.
     * En primera instancia generamos y evaluamos todos los nodos Max del primer
     * nivel. Evaluamos si:
     * - Hacemos 4 en linea en la posicion Max.
     * - El oponente hace 4 en linea en la posicion Max, con lo cual nos ubicamos
     * ahi para evitarlo.
     * - El oponente tiene dos fichas juntas sin extremos donde haya una ficha
     * nuestra, esto puede implicar un 4 en linea doble, por lo tanto nos ubicamos ahi.
     * - Nosotros tenemos dos fichas juntas sin extremos, de forma que podemos
     * hacer 4 en linea doble.
     *
     * Si las situaciones anteriores no se dan, generamos los nodos min para cada
     * nodo Max y evaluamos las posiciones del opnente seleccionando el nodo Max
     * con un criterio pesimista, es decir la mejor de las peores jugadas.
     *
     * @param tablero
     * @return la posicion de la jugada que JugadorAlumno va ahacer.
     */
    @Override
    public int[] jugar(int tablero[][]){
        //alfa = mejor valor que corresponde al mejor Nodo Max.
        //max = valor obtenido para un nodo max, este sera alfa si es mayo que el mismo.

        int alfa = menosinfinito;
        int max = menosinfinito;
        int juegoMax[][];
        int resultado[] = new int[2];

        Posicion posicionAux;
        //Todas las posibles jugadas de Max.
        Vector posiblesJugadasMax = posicionesVacias(tablero);

        juegoMax = copiarMatriz(tablero);

        //Verificamos si en alguna posicion posible de Max se puede hacer 4 en linea.
        for(int i=0; i<posiblesJugadasMax.size(); i++){
            posicionAux = (Posicion)posiblesJugadasMax.elementAt(i);
            juegoMax[posicionAux.getX()][posicionAux.getY()] = jugador;

            if(hace4Linea(juegoMax,posicionAux,jugador)){
                resultado[0] = posicionAux.getX();
                resultado[1] = posicionAux.getY();
                return resultado;
            }

            juegoMax[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        //Verificamos si en alguna posicion posible de Max, que tambien lo es
        //para Min si no la seleccionamos, Min pede hacer 4 en linea despues de nosotros.
        for(int i=0; i<posiblesJugadasMax.size(); i++){
            posicionAux = (Posicion)posiblesJugadasMax.elementAt(i);
            juegoMax[posicionAux.getX()][posicionAux.getY()] = jugador;

            if(hace4Linea(juegoMax,posicionAux,jugadorOponente)){
                resultado[0] = posicionAux.getX();
                resultado[1] = posicionAux.getY();
                return resultado;
            }

            juegoMax[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        //
        for(int i=0; i<posiblesJugadasMax.size(); i++){
            posicionAux = (Posicion)posiblesJugadasMax.elementAt(i);
            juegoMax[posicionAux.getX()][posicionAux.getY()] = jugador;

            if(evita4LineaDoble(juegoMax,posicionAux,jugador)){
                resultado[0] = posicionAux.getX();
                resultado[1] = posicionAux.getY();
                return resultado;
            }

            juegoMax[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        //
        for(int i=0; i<posiblesJugadasMax.size(); i++){
            posicionAux = (Posicion)posiblesJugadasMax.elementAt(i);
            juegoMax[posicionAux.getX()][posicionAux.getY()] = jugador;

            if(evita4LineaDoble(juegoMax,posicionAux,jugadorOponente)){
                resultado[0] = posicionAux.getX();
                resultado[1] = posicionAux.getY();
                return resultado;
            }

            juegoMax[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        for(int i=0; i<posiblesJugadasMax.size(); i++){
            //nivelActual = 0;
            posicionAux = (Posicion)posiblesJugadasMax.elementAt(i);
            juegoMax[posicionAux.getX()][posicionAux.getY()] = jugador;

            max = valorMax(juegoMax, alfa);
            if(max > alfa){
                alfa = max;
                resultado[0] = posicionAux.getX();
                resultado[1] = posicionAux.getY();
            }

            if(max==masinfinito)break;
            juegoMax[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        return resultado;
     }

    /**
     *
     * @param tablero
     * @return
     */
    public boolean evita4LineaDoble(int tablero[][], Posicion pos, int turno){
        int x = pos.getX();
        int y = pos.getY();
        int x1;
        int x2;
        int x3;
        int x4;
        int y1;
        int y2;
        int y3;
        int y4;


        int oponente;
        int jugadorActual;
        if(turno == jugador){
            oponente = jugadorOponente;
            jugadorActual = jugador;
        }else{
            oponente = jugador;
            jugadorActual = jugadorOponente;
        }

        //Horizontales
        y1 = y-1;
        y2 = y-2;
        y3 = y-3;
        if(y2>=1 && tablero[x][y3]!=jugadorActual && tablero[x][y1]==oponente && tablero[x][y2]==oponente)
            return true;
        y1 = y+1;
        y2 = y+2;
        y3 = y+3;
        if(y2<=6 && tablero[x][y3]!=jugadorActual && tablero[x][y1]==oponente && tablero[x][y2]==oponente)
            return true;

        //Verticales
        x1 = x-1;
        x2 = x-2;
        x3 = x-3;
        if(x2>=1 && tablero[x3][y]!=jugadorActual && tablero[x1][y]==oponente && tablero[x2][y]==oponente)
            return true;
        x1 = x+1;
        x2 = x+2;
        x3 = x+3;
        if(x2<=6 && tablero[x3][y]!=jugadorActual && tablero[x1][y]==oponente && tablero[x2][y]==oponente)
            return true;

        //Diagonal1
        x1 = x-1;
        y1 = y-1;
        x2 = x-2;
        y2 = y-2;
        x3 = x-3;
        y3 = y-3;
        if(x2>=1 && y2>=1 && tablero[x3][y3]!=jugadorActual && tablero[x1][y1]==oponente && tablero[x2][y2]==oponente)
            return true;

        x1 = x+1;
        y1 = y+1;
        x2 = x+2;
        y2 = y+2;
        x3 = x+3;
        y3 = y+3;
        if(x2<=6 && y2<=6 && tablero[x3][y3]!=jugadorActual && tablero[x1][y1]==oponente && tablero[x2][y2]==oponente)
            return true;

        //Diagonal2
        x1 = x-1;
        y1 = y+1;
        x2 = x-2;
        y2 = y+2;
        x3 = x-3;
        y3 = y+3;
        if(x2>=1 && y2<=6 && tablero[x3][y3]!=jugadorActual && tablero[x1][y1]==oponente && tablero[x2][y2]==oponente)
            return true;

        x1 = x+1;
        y1 = y-1;
        x2 = x+2;
        y2 = y-2;
        x3 = x+3;
        y3 = y-3;
        if(x2<=6 && y2>=1 && tablero[x3][y3]!=jugadorActual && tablero[x1][y1]==oponente && tablero[x2][y2]==oponente)
            return true;

        //Intermediario horizontal
        y1 = y-1;
        y2 = y+1;
        y3 = y-2;
        y4 = y+2;

        if(y3>=0 && y4<=7 && tablero[x][y1]==oponente && tablero[x][y2]==oponente && tablero[x][y3]!=jugadorActual && tablero[x][y4]!=jugadorActual)
            return true;

        //Intermediario vertical
        x1 = x-1;
        x2 = x+1;
        x3 = x-2;
        x4 = x+2;

        if(x3>=0 && x4<=7 && tablero[x1][y]==oponente && tablero[x2][y]==oponente && tablero[x3][y]!=jugadorActual && tablero[x4][y]!=jugadorActual)
            return true;

        //Intermediario diagonal1
        x1 = x-1;
        y1 = y-1;
        x2 = x+1;
        y2 = y+1;
        x3 = x-2;
        y3 = y-2;
        x4 = x+2;
        y4 = y+2;

        if(x3>=0 && y3>=0 && x4<=7 && y4<=7 && tablero[x1][y1]==oponente && tablero[x2][y2]==oponente && tablero[x3][y3]!=jugadorActual && tablero[x4][y4]!=jugadorActual)
            return true;

        //Intermediario diagonal2
        x1 = x-1;
        y1 = y+1;
        x2 = x+1;
        y2 = y-1;
        x3 = x-2;
        y3 = y+2;
        x4 = x+2;
        y4 = y-2;

        if(x3>=0 && y3<=7 && x4<=7 && y4>=0 && tablero[x1][y1]==oponente && tablero[x2][y2]==oponente && tablero[x3][y3]!=jugadorActual && tablero[x4][y4]!=jugadorActual)
            return true;

        return false;
    }

    /**
     * Metodo que verifica si el jugador paraQuien puede hacer 4 en linea ubicando
     * su ficha en la posicion pos pasada como parametro.
     * @param tablero
     * @param paraQuien
     * @return true si el jugador paraQuien puede hacer 4 en linea.
     *
     */
    public boolean hace4Linea(int tablero[][],Posicion pos,int paraQuien){
        int posX;
        int posY;
        int i;
        int j;
        int x;
        int y;
        int h4L;

        posX = pos.getX();
        posY = pos.getY();

        h4L = 0;

        //Horizontales
        for(i=0; i<4; i++){
            for(j=0; j<4; j++){
                x = posX;
                y = posY - 3 + j + i;

                if(x >= 0 && y >= 0 && x <= 7 && y <= 7){
                    if((tablero[x][y] == paraQuien) || (y == posY)){
                        h4L++;
                    }
                }
            }

            if(h4L == 4){
                return true;
            }else{
                h4L = 0;
            }
        }

        //Verticales
        for(i=0; i<4; i++){
            for(j=0; j<4; j++){
                x = posX - 3 + j + i;
                y = posY;

                if(x >= 0 && y >= 0 && x <= 7 && y <= 7){
                    if((tablero[x][y] == paraQuien) || (x == posX)){
                        h4L++;
                    }
                }
            }

            if(h4L == 4){
                return true;
            }else{
                h4L = 0;
            }
        }

        //Diagonal ppal
        for(i=0; i<4; i++){
            for(j=0; j<4; j++){
                x = posX - 3 + j + i;
                y = posY - 3 + j + i;

                if(x >= 0 && y >= 0 && x <= 7 && y <= 7){
                    if((tablero[x][y] == paraQuien) || (x == posX && y == posY)){
                        h4L++;
                    }
                }
            }

            if(h4L == 4){
                return true;
            }else{
                h4L = 0;
            }
        }

        //Diagonal sec
        for(i=0; i<4; i++){
            for(j=0; j<4; j++){
                x = posX + 3 - j - i;
                y = posY - 3 + j + i;

                if(x >= 0 && y >= 0 && x <= 7 && y <= 7){
                    if((tablero[x][y] == paraQuien) || (x == posX && y == posY)){
                        h4L++;
                    }
                }
            }

            if(h4L == 4){
                return true;
            }else{
                h4L = 0;
            }
        }

        return false;
    }

    /**
     * Método que devuelve un vector con las posiciones vacias (iguales a 0) del tablero de juego.
     * @param tablero.
     * @return un vector con las posiciones xy vacias del tablero de juego.
     *
     */
    public Vector posicionesVacias(int tablero[][]){
        Vector posiciones  = new Vector();
        for(int i=0; i<tablero.length; i++){
            for(int j=0; j<tablero[0].length; j++){
                if(tablero[i][j]==0){
                    posiciones.add(new Posicion(i, j));
                }
            }
        }
        return posiciones;
    }

    /**
     * Metodo que recibe un tablero que representa una posible jugada de Max, apartir de este tablero genera
     * los nodos Min, posibles jugadas del oponente, y para cadauno de ellos calcula
     * la diferencia entre la cantidad de 4 en linea que puede hacer el jugador y la cantidad de
     * 4 en linea que puede hacer el oponente. Si esta diferencia es positiva significa una ventaja del jugador sobre el oponente
     * y viceversa si la diferencia es negativa.
     * El valorMax que se devuelve es la minima diferencia calculada es decir la peor situacion para
     * el jugador.
     *
     * @param tablero
     * @param alfa
     * @return
     */
    public int valorMax(int tablero[][],int alfa){
        nivelActual++;
        Vector posiblesJugadasMin = posicionesVacias(tablero);
        Posicion posicionAux;
        int juegoMin[][];
        int min = masinfinito;
        int diferencia = masinfinito;

        int beta = masinfinito;

        for(int i=0; i<posiblesJugadasMin.size(); i++){
            juegoMin = copiarMatriz(tablero);
            posicionAux = (Posicion)posiblesJugadasMin.elementAt(i);

            juegoMin[posicionAux.getX()][posicionAux.getY()] = jugadorOponente;

            if(nivelActual == cantNiveles-1){
                if(hace4Linea(juegoMin,posicionAux,jugadorOponente)){
                    diferencia = menosinfinito;
                }
                else{
                    diferencia = calcularDiferencia(juegoMin);
                }
            }else{
                diferencia = valorMin(juegoMin, beta);
                if(diferencia < beta){
                    beta = diferencia;
                }
            }

            if(diferencia <= alfa){
                min = diferencia;
                break;
            }else if(diferencia <= min){
                min = diferencia;
            }
            juegoMin[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        nivelActual--;
        return min;
    }

    /**
     *
     * @param tablero
     * @param beta
     * @return
     */
    public int valorMin(int tablero[][], int beta){
        nivelActual++;

        Vector posiblesJugadasMax = posicionesVacias(tablero);
        Posicion posicionAux;
        int diferencia = masinfinito;
        int max = masinfinito;

        int alfa = menosinfinito;

        for(int i=0; i<posiblesJugadasMax.size(); i++){
            int juegoMax[][] = copiarMatriz(tablero);
            posicionAux = (Posicion)posiblesJugadasMax.elementAt(i);
            juegoMax[posicionAux.getX()][posicionAux.getY()] = jugador;

            if(nivelActual == cantNiveles -1){
                if(hace4Linea(juegoMax, posicionAux, jugador)){
                    return masinfinito;
                }else{
                    diferencia = calcularDiferencia(juegoMax);
                }
            }else{
                diferencia = valorMax(juegoMax, alfa);
                if(diferencia > alfa){
                    alfa = diferencia;
                }
            }

            if(diferencia<= beta){
                return diferencia;
            }else{
                if(diferencia < max){
                    max = diferencia;
                }
            }

            juegoMax[posicionAux.getX()][posicionAux.getY()] = 0;
        }

        nivelActual--;
        return max;
    }

    /**
     * Metodo que calcula la diferencia entre la diferencia entre la cantidad de 4
     * en linea que puede hacer el jugador y la cantidad de 4 en linea que puede hacer
     * el oponente.
     *
     * @param tablero
     * @return
     */
    public int calcularDiferencia(int tablero[][]){
        int diferencia = 0;

        int sumaPropia;
        int sumaContrario;

        sumaPropia = calcularLineasDiagonales(tablero,true) + calcularLineasHorizontales(tablero,true) + calcularLineasVerticales(tablero,true);
        sumaContrario = calcularLineasDiagonales(tablero,false) + calcularLineasHorizontales(tablero,false) + calcularLineasVerticales(tablero,false);
        diferencia = sumaPropia - sumaContrario;

        return diferencia;
    }

    /**
     * Metodo que calcula la cantidad de 4 en linea diagonales que puede hacer un jugador.
     * @param tablero
     * @param turnoJugador
     * @return cantidad de 4 en linea diagonales.
     */
    public int calcularLineasDiagonales(int tablero[][], boolean turnoJugador){
        int cant = 0;
        int x;
        int y;
        int oponente;
        if(turnoJugador) {
            oponente = jugadorOponente;
        }else{
            oponente = jugador;
        }

        for(int i=0; i<=tablero.length - 4;i++){
            for(int j=0; j<=tablero.length - 4;j++){
                x = i;
                y = j;
                if(tablero[x][y]!=oponente && tablero[x+1][y+1]!=oponente && tablero[x+2][y+2]!=oponente && tablero[x+3][y+3]!=oponente){
                    cant++;
                }
                y = y+3;
                if(tablero[x][y]!=oponente && tablero[x+1][y-1]!=oponente && tablero[x+2][y-2]!=oponente && tablero[x+3][y-3]!=oponente){
                    cant++;
                }
            }
        }
        return cant;
    }

    /**
     * Metodo que calcula la cantidad de 4 en linea horizontales que puede hacer
     * un jugador.
     * @param tablero
     * @param oponente
     * @return la cantidad de 4 en linea horizontales.
     */
    public int calcularLineasHorizontales(int tablero[][], boolean turnoJugador){
        int cant=0;
        int x;
        int y;
        int oponente;
        if(turnoJugador) {
            oponente = jugadorOponente;
        }else{
            oponente = jugador;
        }

        for(int i=0; i< tablero.length;i++){
            for(int j=0; j<= tablero.length-4;j++){
                x = i;
                y = j;
                if(tablero[x][y]!=oponente && tablero[x][y+1]!=oponente && tablero[x][y+2]!=oponente && tablero[x][y+3]!=oponente){
                    cant++;
                }
            }
        }
        return cant;
    }

    /**
     * Metodo que calcula la cantidad de 4 en linea verticales que puede hacer un
     * jugador.
     * @param tablero
     * @param oponente
     * @return la cantidad de 4 en linea horizontales.
     */
    public int calcularLineasVerticales(int tablero[][], boolean turnoJugador){
        int cant=0;
        int x;
        int y;
        int oponente;
        if(turnoJugador) {
            oponente = jugadorOponente;
        }else{
            oponente = jugador;
        }

        for(int i=0; i<=tablero.length-4; i++){
            for(int j=0; j< tablero.length; j++){
                x = i;
                y = j;

                if(tablero[x][y]!=oponente && tablero[x+1][y]!=oponente && tablero[x+2][y]!=oponente && tablero[x+3][y]!=oponente){
                    cant++;
                }
            }
        }
        return cant;
    }

    /**
     * Metodo que devuelve una copia de una matriz.
     * @param original
     * @return una copia de la matriz recibida como parámetro.
     *
     */
    public int[][] copiarMatriz(int original[][]){
        int copia[][] = new int[original.length][original[0].length];//Ver si tablero.length = cantidad de filas y tablero[0].length devuelve la cantidad de columnas.
        for(int i=0; i<original.length; i++){
            for(int j=0; j<original[0].length; j++){
                copia[i][j]=original[i][j];
            }
        }
        return copia;
    }

}